package com.object.utils;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.object.utils.MapEmptyToolUtil;
import lombok.Data;
import lombok.SneakyThrows;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 自定义的构建查询
 *
 * @param <T>
 */
public class MyQueryWrapper<T> {
    protected static Logger log = Logger.getLogger(String.valueOf(MyQueryWrapper.class));
    private String key;
    private String value;

    private QueryWrapper<T> wrapper = new QueryWrapper<T>();

    private ConcurrentHashMap<String, String> fieldMap = new ConcurrentHashMap<>();

    private static Integer pageNum;

    private static Integer pageSize;

    private static HashSet<String> excludeFields = new HashSet<>();

    static {
        excludeFields.add("serialVersionUID");
    }


    public MyQueryWrapper() {
    }

    /**
     * 构建查询
     *
     * @param map
     */
    private void init(Map<String, Object> map, boolean checkThatTheFieldExists) {
        if (map.containsKey("key")) {
            key = String.valueOf(map.get("key"));
            map.remove("key");
        }
        if (map.containsKey("value")) {
            value = String.valueOf(map.get("value"));
            map.remove("value");
        }
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (checkThatTheFieldExists) {
                if (doesTheFieldExist(entry.getKey())) {
                    if (!MapEmptyToolUtil.isNull(entry.getKey()) && !MapEmptyToolUtil.isNull(String.valueOf(entry.getValue()))) {
                        commCreateSelect(entry);
                    }
                }
            } else {
                commCreateSelect(entry);
            }
        }
    }

    private void commCreateSelect(Map.Entry<String, Object> entry) {
        if (!MapEmptyToolUtil.isNull(entry.getKey()) && !MapEmptyToolUtil.isNull(String.valueOf(entry.getValue()))) {
            if (isNumeric(String.valueOf(entry.getValue())) || isLetterDigit(String.valueOf(entry.getValue()))) {
                wrapper.eq(camel2under(entry.getKey()), entry.getValue());
            } else {
                wrapper.like(camel2under(entry.getKey()), entry.getValue());
            }
        }
    }

    /**
     * 判断当前查询的字段是否存在
     */
    private boolean doesTheFieldExist(String key) {
        return fieldMap.containsKey(key);
    }

    /**
     * 初始化 字段
     */
    private void initField(Class<T> tClass) {
        Field[] fields = tClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().hashCode() == "serialVersionUID".hashCode()
                    || field.getName().hashCode() == "createTime".hashCode()
                    || field.getName().hashCode() == "createUser".hashCode()
                    || field.getName().hashCode() == "modifyId".hashCode()
                    || field.getName().hashCode() == "modifyUser".hashCode()
                    || field.getName().hashCode() == "modifyIp".hashCode()
                    || field.getName().hashCode() == "modifyTime".hashCode()
            ) {
                continue;
            }
            fieldMap.put(field.getName(), field.getName());
        }
    }

    public MyQueryWrapper(Map<String, Object> map, Class<T> tClass) {
        /**
         * 初始化 查询字段的map  并且去空
         */
        ConcurrentHashMap<String, Object> maps = MapEmptyToolUtil.emptyTool(map);
        /**
         * 加载bean的字段名
         */
        initField(tClass);
        /**
         * 构建 查询条件
         */
        init(maps, true);

        loadPage();
    }


    public MyQueryWrapper(Class<T> tClass, T t) {
        /**
         * 加载bean的字段名
         */
        ConcurrentHashMap<String, Object> maps = findNotNullField(tClass, t);
        /**
         * 构建 查询条件
         */
        init(maps, false);

        loadPage();
    }

    public MyQueryWrapper(Class<T> tClass) {
        /**
         * 加载bean的字段名
         */
        initField(tClass);

        loadPage();
    }

    /**
     * 加载分页
     */
    public static void loadPage() {
        HttpServletRequest request = getRequestAttributes().getRequest();
        pageNum = Integer.valueOf(request.getParameter("page") == null ? "1" : request.getParameter("page"));
        pageSize = Integer.valueOf(request.getParameter("pageSize") == null ? "10" : request.getParameter("pageSize"));
    }

    public static Integer getPageNum() {
        return pageNum;
    }

    public static void setPageNum(Integer pageNum) {
        MyQueryWrapper.pageNum = pageNum;
    }

    public static Integer getPageSize() {
        return pageSize;
    }

    public static void setPageSize(Integer pageSize) {
        MyQueryWrapper.pageSize = pageSize;
    }

    public static ServletRequestAttributes getRequestAttributes() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }

    /**
     * 处理 key 和value
     *
     * @return
     */

    public QueryWrapper<T> ConstructQuery() {
        if (!MapEmptyToolUtil.isNull(value) && !MapEmptyToolUtil.isNull(key)) {
            if (isNumeric(String.valueOf(value)) || isLetterDigit(String.valueOf(value))) {
                wrapper.eq(camel2under(key), value);
            } else {
                wrapper.like(camel2under(key), value);
            }
        }
        return wrapper;
    }

    /**
     * 分页对象
     *
     * @param service
     * @return
     */
    public Page<T> selectPage(IService<T> service, AbstractWrapper wrapper) {
        Page<T> page = new Page<>(pageNum, pageSize);
        return service.page(page, wrapper);
    }

    /**
     * 分页对象
     *
     * @param service
     * @return
     */
    public Q selectPageResponse(IService<T> service, AbstractWrapper wrapper) {
        Page<T> page = new Page<>(pageNum, pageSize);
        Page<T> page1 = service.page(page, wrapper);
        return Q.ok().q("list", page1.getRecords())
                .q("counts", page1.getTotal())
                .q("total", page1.getTotal())
                .q("pageSize", page1.getSize())
                .q("pages", page1.getPages())
                .q("page", page1.getCurrent());
    }

    /**
     * 分页对象
     *
     * @param service
     * @return
     */
    public Q<List<T>> pageResponse(IService<T> service, AbstractWrapper wrapper) {
        Page<T> page = new Page<>(pageNum, pageSize);
        Page<T> page1 = service.page(page, wrapper);
        return new Q<List<T>>().ok().q("page",new PageUtils(page1));
    }

    /**
     * 自己处理key和value
     *
     * @param fields 字段集合
     * @param query  字段对应的查询类型
     * @return
     */
    public QueryWrapper<T> ConstructQuery(String[] fields, String[] query) {
        if (fields.length != query.length) {
            log.warning("Error :fields|query 字段长度不一致 }！");
        }
        CustomProcessingKeyValue(fields, query);

        return wrapper;
    }

    /**
     * 功能：驼峰命名转下划线命名
     * 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
     */
    public static String camel2under(String c) {
        String separator = "_";
        c = c.replaceAll("([a-z])([A-Z])", "$1" + separator + "$2").toLowerCase();
        return c;
    }

    private void CustomProcessingKeyValue(String[] fields, String[] query) {
        for (int i = 0; i < fields.length; i++) {
            if (key.equals(fields[i])) {
                //找到匹配的字段 需要找到对应的查询条件
                String pram = query[i];
                matchQueryType(pram, fields[i]);
            }
        }
    }


    /**
     * 匹配查询的具体
     *
     * @param pram
     * @param field
     */
    private void matchQueryType(String pram, String field) {
        switch (pram) {
            case "=":
                wrapper.eq(field, value);
                break;
            case "%":
                wrapper.like(field, value);
                break;
            case ">":
                wrapper.gt(field, value);
                break;
            case ">=":
                wrapper.ge(field, value);
                break;
            case "<":
                wrapper.lt(field, value);
                break;
            case "<=":
                wrapper.le(field, value);
                break;
            case "!=":
                wrapper.ne(field, value);
                break;
        }
    }


    /**
     * 判断是不是纯数字
     *
     * @param str
     * @return
     */
    private static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

    /**
     * 判断是不是由数字和字母组成
     *
     * @param str
     * @return
     */
    private static boolean isLetterDigit(String str) {
        String regex = "^[a-z0-9A-Z]+$";
        return str.matches(regex);
    }


    public String getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }

    public QueryWrapper ConstructQuery(String key, String value) {
        if (!MapEmptyToolUtil.isNull(value) && !MapEmptyToolUtil.isNull(key)) {
            if (fieldMap.containsKey(key)) {
                //存在
                if (isNumeric(value) || isLetterDigit(value)) {
                    wrapper.eq(camel2under(key), value);
                } else {
                    wrapper.like(camel2under(key), value);
                }
            }
        }
        return wrapper;
    }

    /**
     * 找出查询对象里面有值得属性
     *
     * @param aClass 对象class
     * @param t      对象
     */
    public ConcurrentHashMap<String, Object> findNotNullField(Class aClass, T t) {
        Field[] declaredFields = aClass.getDeclaredFields();
        ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
        for (Field field : declaredFields) {
            if (!excludeFields.contains(field.getName())) {
                field.setAccessible(true);
                Object o = null;
                try {
                    o = field.get(t);
                } catch (IllegalAccessException e) {
                    log.info("字段解析错误！err：" + field.getName());
                }
                if (o != null) map.put(field.getName(), o);
            }
        }
        return map;
    }

}
